#include "common.h"
#include "bsp_flash.h"
#include "crc_calc.h"
#include "app_param_store.h"
#include "shell.h"
#define ALIGN_BYTE      (4)
#define ALIGN(x)        ((x + ALIGN_BYTE - 1) & (~(ALIGN_BYTE - 1)))    

#define FLASH_START_OFFSET          0X00            //FLASH起始偏移
#define FLASH_TOTAL_SIZE            0x20000         //FLASH总的大小
#define FLASH_SECTOR_SIZE           0x800           //FLASH扇区大小

//flash的最后两个扇区用于key val的存储
#define STORE_AREA_START    (FLASH_START_OFFSET + FLASH_TOTAL_SIZE - (2 * FLASH_SECTOR_SIZE))   
#define STORE_AREA_END      (FLASH_START_OFFSET + FLASH_TOTAL_SIZE - 1)

#define FLASH_MAGIC1                      (0xDEADBEEF)
#define FLASH_MAGIC2                      (~0xDEADBEEF)



typedef struct store_handler_s{
    uint8_t valid;
    uint8_t erase;
    store_msg_t* msg;
    uint32_t write_address;
    uint32_t program_cnt;
}store_handler_t;

store_handler_t store_handler;

#define MSG_TYPE 0X00


void* find_newest_value(void)
{
    uint32_t address_prv = 0xffffffff;
    uint32_t address_new = 0xffffffff;
    uint32_t program_cnt_max = 0;
    store_msg_t * msg = NULL;
    int start = STORE_AREA_START;
    for(; start < STORE_AREA_END; start += ALIGN_BYTE)
    {
        if(FLASH_MAGIC1 == EFRFLASH_ReadWord(start))
        {
            msg = (store_msg_t *)start;
            if((FLASH_MAGIC2 == msg->magic2) && (MSG_TYPE == msg->type))
            {
                if(program_cnt_max <= msg->program_cnt)
                {
                    program_cnt_max = msg->program_cnt;
                    address_prv = address_new;
                    address_new = start;
                }
            }
        }
    }
    if(0xffffffff == address_new)
    {
        //没有找到有用的消息
        return NULL;
    }
    msg = (store_msg_t *)address_new;
    uint16_t crc16 = CRC16_calculate(&msg->type, sizeof(store_msg_t) - (4 + 4 + 4));

    if(crc16 == msg->crc)
    {
        return (void*)msg;
    }

    //最新的消息,crc校验错误,处理次新的消息
    if(0xffffffff == address_prv)
    {
        //没有找到有用的消息
        return NULL;
    }
    msg = (store_msg_t *)address_prv;
    crc16 = CRC16_calculate(&msg->type, sizeof(store_msg_t) - (4 + 4 + 4));

    if(crc16 == msg->crc)
    {
        return (void*)msg;
    }
    return NULL;
}

static uint32_t store_position_offset(uint32_t current, uint8_t offset, uint8_t need, uint8_t *is_skip)
{
    uint32_t ret;
    if((current&((FLASH_SECTOR_SIZE - 1))) + (offset + need) >= FLASH_SECTOR_SIZE)
    {
        current += FLASH_SECTOR_SIZE;
        ret = current &(~(FLASH_SECTOR_SIZE - 1));
        if(is_skip) *is_skip = true;
    }
    else
    {
        ret = current + offset;
        if(is_skip) *is_skip = false;
    }

    if(ret > STORE_AREA_END)
    {
        ret = STORE_AREA_START;
    }
    return ret;
}

void* store_msg_init(void)
{
    void* ret = find_newest_value();
    if(ret)
    {
        store_handler.valid = true;
        uint8_t skip;
        //记录消息写入地址
        store_handler.write_address = store_position_offset((uint32_t)ret, ALIGN(sizeof(store_msg_t)), ALIGN(sizeof(store_msg_t)), &skip);
        if(skip)
        {
            store_handler.erase = true;
        }
        store_handler.msg = (void*)ret;
//        memcpy(&store_handler.msg, (void*)ret, sizeof(store_msg_t));
        store_handler.program_cnt = store_handler.msg->program_cnt;
    }
    else
    {
        store_handler.valid = false;
        store_handler.write_address = STORE_AREA_START;
//        memset(&store_handler.msg, 0, sizeof(store_msg_t));
        store_handler.msg = NULL;
        store_handler.program_cnt = 0;
    }
    return ret;
}

void store_msg_write(void* msg)
{
    uint8_t skip;

    if((false == store_handler.valid) || store_handler.erase)
    {
        store_handler.valid = true;
        //需要清除当前工作扇区
        store_handler.erase = false;
        elog_warn("erase sector address:%08x", store_handler.write_address);
        if(WriteFlashPrepare(store_handler.write_address, FLASH_SECTOR_SIZE))
        {
            Erase_Parameter_Sector(store_handler.write_address);
        }
    }
    do
    {
        //可以写入消息
        if(!WriteFlashPrepare(store_handler.write_address, ALIGN(sizeof(store_msg_t))))
        {
            EFRFLASH_Write(store_handler.write_address, msg, ALIGN(sizeof(store_msg_t)) >> 2);
            break;
        }

        store_handler.write_address = store_position_offset(store_handler.write_address, ALIGN(sizeof(store_msg_t)), ALIGN(sizeof(store_msg_t)), &skip);
        if(skip)
        {
            if(WriteFlashPrepare(store_handler.write_address, FLASH_SECTOR_SIZE))
            {
                Erase_Parameter_Sector(store_handler.write_address);
            }
            EFRFLASH_Write(store_handler.write_address, msg, ALIGN(sizeof(store_msg_t)) >> 2);
            break;
        }
    } while (1);
    
    store_handler.write_address = store_position_offset(store_handler.write_address, ALIGN(sizeof(store_msg_t)), ALIGN(sizeof(store_msg_t)), &skip);
    //扇区跳转,下次写入时需要erase后写入
    if(skip) 
    {
        store_handler.erase = true;
    }
}




void store_msg_generate(store_msg_t* msg, uint32_t port, uint32_t time_remain)
{
    msg->magic1 = FLASH_MAGIC1;
    msg->type = MSG_TYPE;
    msg->lens = sizeof(store_msg_t);
    msg->program_cnt = ++store_handler.program_cnt;
    msg->port = port;
    msg->time_remain = time_remain;
    msg->crc = CRC16_calculate(&msg->type, sizeof(store_msg_t) - (4 + 4 + 4));
    msg->magic2 = FLASH_MAGIC2;
}


//app_flash_test example

void store_info_query_demo(int argc, char *argv[])
{
    void* ret = find_newest_value();
    if(ret)
    {
        store_msg_t* msg = (void*)ret;
        elog_info("address:%08x", msg);
        elog_info("program_cnt:%08x", msg->program_cnt);
        elog_info("port:%08x", msg->port);
        elog_info("time_remain:%08x", msg->time_remain);
    }
    else
    {
        elog_error("Not found avaliable store info");
    }
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN, store_info_query_demo, store_info_query_demo,"store info query demo");




void store_area_erase_demo(int argc, char *argv[])
{
    if(3 != argc)
    {
        return;
    }
    uint32_t address;
    uint32_t lens;

    sscanf(argv[1], "%x", &address);
    sscanf(argv[2], "%x", &lens);

    elog_info("erase:%08x-%x", address, lens);
    uint32_t zero = 0;
    for(int i = 0; i < ALIGN(lens) >> 2; i++)
    {
        EFRFLASH_Write(address, &zero, 1);
        address += 4;
    }
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN, store_area_erase_demo, store_area_erase_demo,"store area erase demo");
#include "app_task.h"
void store_info_write_demo(int argc, char *argv[])
{
    extern void flash_operation_notify(flash_operation_t oper);
    if(3 != argc && 4 != argc)
    {
        return;
    }
    flash_operation_t operation;
    int32_t value = atoi(argv[2]);
    if(FLASH_TIME_REMAIN != atoi(argv[1]))
    {
        operation.type = FLASH_TIME_REMAIN;
        operation.time_remain = value;
    }
    else
    {
        operation.type = FLASH_PIN_MAP;
        operation.port = value;
    }

    int repeat = 1;
    if(4 == argc)
    {
        repeat = atoi(argv[3]);
    }
    
    for(int i = 0; i < repeat; i++)
    {
        flash_operation_notify(operation);
    }
    
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RETURN, store_info_write_demo, store_info_write_demo,"store area write demo");

#if 0
#include "common.h"
#include "app_flash.h"
#include "bsp_flash.h"
#define ALIGN_BYTE      (4)
#define ALIGN(x)        ((x + ALIGN_BYTE - 1) & (~(ALIGN_BYTE - 1)))    

/* magic word(`F`, `D`, `B`, `1`) */
#define SECTOR_MAGIC_WORD                        0x30424446
/* magic word(`K`, `V`, `0`, `0`) */
#define KV_MAGIC_WORD                            0x3030564B

#define FLASH_START_OFFSET          0X00            //FLASH起始偏移
#define FLASH_TOTAL_SIZE            0x20000         //FLASH总的大小
#define FLASH_SECTOR_SIZE           0x800           //FLASH扇区大小

//flash的最后两个扇区用于key val的存储
#define STORE_AREA_START    (FLASH_START_OFFSET + FLASH_TOTAL_SIZE - (2 * FLASH_SECTOR_SIZE))   
#define STORE_AREA_END      (FLASH_START_OFFSET + FLASH_TOTAL_SIZE - 1)

enum fdb_kv_status {
    FDB_KV_UNUSED,
    FDB_KV_PRE_WRITE,
    FDB_KV_WRITE,
    FDB_KV_PRE_DELETE,
    FDB_KV_DELETED,
    FDB_KV_ERR_HDR,
#define FDB_KV_STATUS_NUM                        6
};

struct kv_hdr_data {
    uint8_t status_table[1];  /**< KV node status, @see fdb_kv_status_t */
    uint32_t magic;                              /**< magic word(`K`, `V`, `4`, `0`) */
    uint32_t len;                                /**< KV node total length (header + name + value), must align by FDB_WRITE_GRAN */
    uint32_t crc16;                              /**< KV node crc32(name_len + data_len + name + value) */
    uint8_t name_len;                            /**< name length */
    uint32_t value_len;                          /**< value length */
};


/* the flash sector store status */
enum fdb_sector_store_status {
    FDB_SECTOR_STORE_UNUSED,
    FDB_SECTOR_STORE_EMPTY,
    FDB_SECTOR_STORE_USING,
    FDB_SECTOR_STORE_PRE_FULL,
    FDB_SECTOR_STORE_FULL,
#define FDB_SECTOR_STORE_STATUS_NUM              5
};

struct sector_hdr_data {
    struct {
        uint8_t store[1];  /**< sector store status @see fdb_sector_store_status_t */
    } status_table;
    uint32_t magic;                              /**< magic word(`E`, `F`, `4`, `0`) */
};
typedef struct sector_hdr_data *sector_hdr_data_t;

struct flash_program port_manage;
struct flash_program time_manage;

#define PORT_MANAGE     "port"
#define TIME_MANAGE     "time"


uint32_t write_sector_address = 0;


size_t _fdb_get_status(uint8_t status_table[], size_t status_num)
{
    size_t i = 0, status_num_bak = --status_num;

    while (status_num --) {
        /* get the first 0 position from end address to start address */
        if ((status_table[status_num / 8] & (0x80 >> (status_num % 8))) == 0x00) {
            break;
        }
        i++;
    }

    return status_num_bak - i;
}

int flash_db_init(void)
{
    int start = STORE_AREA_START;
    for(; start < STORE_AREA_END; start += FLASH_SECTOR_SIZE)
    {
        struct sector_hdr_data *sector = (struct sector_hdr_data *)start;
        if(SECTOR_MAGIC_WORD == sector.magic)
        {
            size_t status = _fdb_get_status(sector->status_table.store, FDB_SECTOR_STORE_STATUS_NUM);
            if(FDB_SECTOR_STORE_USING == status)
            {
                write_sector_address = start;
            }
            else if(FDB_SECTOR_STORE_PRE_FULL == status)
            {
                //需要将当前待满的扇区中有效的value拷贝到下一个扇区
            }
        }
        else
        {
            if(write_sector_address)
            {
                //do nothing!!
            }
            else
            {
                //记录第一个可用扇区的位置
                write_sector_address = start;
            }
            //删除当前扇区
            // erase();
            //并写入默认扇区头
        }
    }
}


void* find_newest_value(const char* key)
{
    int start = STORE_AREA_START;
    for(; start < STORE_AREA_END; start += ALIGN_BYTE)
    {
        if(FLASH_MAGIC == EFRFLASH_ReadWord(start))
        {
            
            FDB_KV_WRITE
        }
    }
}


#endif
